package se.webbzon.boltzmann.game.npc;

import javax.vecmath.Point3d;

import se.webbzon.boltzmann.effect.SurpriseEffect;
import se.webbzon.boltzmann.game.object.player.AbstractPlayerCharacter;

public class StalkingDuty extends WalkingDuty {
	
	// The minimal range between the player and the non-player character
	private static final int stepsBetweenStalking = 15;
	
	// The line of sight for this duty
	private final double lineOfSight;
	
	// The number of steps until next stalking
	private int stalkingCooldown;
	
	// Whatever the non-player character is returning to the start position
	private boolean stalking, returning;

	/*============================================================
	Constructors
	============================================================*/
	
	/** Creates a new stalking duty. **/
	public StalkingDuty(GameNPC npc,
						double lineOfSight,
						double walkingRange, 
						double stoppingRange) {
		super(npc,walkingRange,stoppingRange);
		this.lineOfSight = lineOfSight;
		returning = false;
		stalking = false;
		stalkingCooldown = stepsBetweenStalking;
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	@Override public void onReachTarget() {
		super.onReachTarget();
		returning = false;
	}
	
	@Override public void onBlockedPath() {
		super.onBlockedPath();
		GameNPC npc = (GameNPC) getNPC();
		final Point3d origin = new Point3d();
		getOrigin(origin);
		setTarget(origin.x,origin.z,npc.getWalkSpeed());
		returning = true;
		stalkingCooldown = 30;
		stalking = false;
		if (npc.getMask() != null && !motionFree(npc)) {
			setTarget(	npc.getX() - npc.getXVelocity(), 
						npc.getZ() - npc.getZVelocity(),
						npc.getWalkSpeed());
			returning = false;
		}
	}
	
	@Override public void onBegin() {
		super.onBegin();
	}

	@Override public void onStep() {
		GameNPC npc = (GameNPC) getNPC();
		
		// Retrieve the the closest player
		AbstractPlayerCharacter player = npc.closestPlayer();
		final double distance = npc.distanceTo(player);
		
		// Retrieve the start position
		final Point3d origin = new Point3d();
		getOrigin(origin);
		final double distanceToStart = player.distanceTo(origin);
		final boolean stalk =	!npc.isHostile() ||
								(npc.isHostile() && !player.isInvulnerable());
		
		if (distanceToStart > getRange()) {
			// Returns to start position
			if (stalking && !returning) {
				returning = true;
				stalking = false;
				setTarget(origin.x,origin.z,npc.getWalkSpeed());
			}
			super.onStep();
			
		} else if (stalk && distance > getIgnoreRange() && distance < lineOfSight) {
			// Stalk the player
			if (stalkingCooldown == 0) {
				setTarget(player.getX(),player.getZ(),npc.getRunSpeed());
				if (stalking == false) {
					new SurpriseEffect(getNPC()).launch(npc.getWorld());
					stalking = true;
				}
			} else if (npc.getMask() != null && !motionFree(npc))
				onBlockedPath();
		} else 
			// Patrol (walk around)
			super.onStep();
		
		// Update the stalking cooldown
		if (stalkingCooldown == 0)
			stalkingCooldown = stepsBetweenStalking;
		else
			stalkingCooldown--;
	}
	
}
